using System;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Xml;
using com.paypal.sdk.resources;
using com.paypal.sdk.util;

namespace com.paypal.sdk.core
{
	/// <summary>
	/// Contains application-wide configuration settings.  Singleton class accessed through
	/// Config.Instance.  Configuration settings are read from the web.config/Console.exe.config
	/// file
	/// </summary>
	public class Config
	{
		static Config()
		{
		}

		private Config()
		{
			XmlNode configSection;
			if (IsApplicationBaseSet)
			{
				ManualConfig manualConfig = new ManualConfig(ApplicationBase);
				manualConfig.Run();
				configSection = manualConfig.ExternalFileConfigSection;
			}
			else
			{
				configSection = AppDomainConfigSection;
			}

			this.TrustAll = ConfigDefaults.TRUST_ALL;
			this.MaximumRetries = ConfigDefaults.MAXIMUM_RETRIES;

			XmlNode defaultEndpointsNode = Utils.CreateXmlNode(DefaultEndpointsStream);
			this.endpoints = ConfigSectionParser.ReadEndpoints(defaultEndpointsNode);
			// try to read overrides
			if (configSection != null)
			{
				ConfigSectionParser parser = new ConfigSectionParser(configSection);
				if (parser.IsTrustAllSpecified)
				{
					this.TrustAll = parser.TrustAll;
				}
				if (parser.IsMaximumRetriesSpecified)
				{
					this.MaximumRetries = parser.MaximumRetries;
				}
				if (parser.IsEndpointsSpecified)
				{
					this.endpoints = parser.Endpoints;
				}
			}
		}

		private static XmlNode AppDomainConfigSection
		{
			get { return (XmlNode) ConfigurationSettings.GetConfig(PAYPAL_TAG); }
		}

		/// <summary>
		/// tag defining paypal config section in web.config/console.exe.config
		/// </summary>
		public const string PAYPAL_TAG = "paypal";

		private static string applicationBase = string.Empty;

		/// <summary>
		/// ApplicationBase defines directory where web.config is stored.  
		/// If you are calling the SDK from COM, you need to set
		/// this property. 
		/// </summary>
		public static string ApplicationBase
		{
			get
			{
				return applicationBase;
			}
			set
			{
				if (!IsApplicationBaseSet)
				{
					if (value == null)
					{
						throw new ArgumentNullException("applicationBase");
					}
					string fullPath = Path.GetFullPath(value.Trim());
					if (!Directory.Exists(fullPath))
					{
						throw new FileNotFoundException(MessageResources.GetMessage("SET_APPBASE_MULTIPLE") + " value : '" + value + "', full path : '" + fullPath + "'.");
					}
					applicationBase = fullPath;
				}
			}
		}

		private static readonly object syncRoot = new object();
		private static Config instance = null;

		/// <summary>
		/// Use this property to access the Config singleton.
		/// </summary>
		public static Config Instance
		{
			get
			{
				lock (syncRoot)
				{
					if (instance == null)
					{
						instance = new Config(); 
					}
				}
				return instance;
			}
		}

		private readonly Hashtable endpoints;
		/// <summary>
		/// TrustAll flag.  If true, all certificates are validated.
		/// </summary>
		public readonly bool TrustAll = ConfigDefaults.TRUST_ALL;

		/// <summary>
		/// MaximumRetries setting.  Number of times SOAP requests are retried.
		/// </summary>
		public readonly int MaximumRetries = ConfigDefaults.MAXIMUM_RETRIES;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="environment"></param>
		/// <param name="port"></param>
		/// <param name="isThreeToken"></param>
		/// <returns></returns>
		public Endpoint GetEndpoint(string environment, string port, bool isThreeToken)
		{
			return (Endpoint) this.endpoints[ConfigSectionParser.GetEndpointKey(environment, port, isThreeToken)];
		}

		private static Stream DefaultEndpointsStream
		{
			get
			{
                string endpoints = ".ButtonManager.PlatformSDK.Paypal_Base.resources.paypal-endpoints"
			#if NVP
				+ "-nvp"
			#else
			#endif
					+ ".xml";
				return ResourceHelper.GetResourceStream(endpoints);
			}
		}

		private static bool IsApplicationBaseSet
		{
			get { return ApplicationBase != string.Empty; }
		}
	}
}
